﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace FerrSimplicity
{
    /// <summary>
    /// This class manages the active and inactive game states, enabling and disabling them,
    /// updating and drawing them.
    /// </summary>
    public class SystemGameStates
    {
        #region Singleton
        private static SystemGameStates mInstance = null;
        public  static SystemGameStates Instance
        {
            get { if (mInstance == null) mInstance = new SystemGameStates(); return mInstance; }
        }
        private SystemGameStates() { mStates = new List<BaseGameState>(); }
        #endregion

        #region Fields
        private List<BaseGameState> mStates;
        #endregion

        #region Methods
        /// <summary>
        /// This adds a game state to the list. It will throw an exception if a game state
        /// of the same Type already exists in the list.
        /// </summary>
        /// <param name="aGameState">An instance of a unique game state</param>
        public void AddState(BaseGameState aGameState)
        {
            // we don't support multiple states of the same type, as we address using Type
            for (int i = 0; i < mStates.Count; i++)
                if (mStates[i].GetType() == aGameState.GetType())
                    throw new Exception("There is already a game state of this type registered!");

            mStates.Add(aGameState);
        }
        /// <summary>
        /// Removes the game state of the specified type, this will throw an Exception 
        /// if there is no state that matches.
        /// </summary>
        /// <typeparam name="T">The exact Type of the game state to remove.</typeparam>
        public void RemoveState<T>() where T : BaseGameState
        {
            Type type = typeof(T);

            // find the state that matches the given type
            for (int i = 0; i < mStates.Count; i++)
            {
                if (mStates[i].GetType() == type)
                {
                    // remove it from the list properly
                    if (mStates[i].Active)
                        mStates[i].Suspend(false);
                    mStates.RemoveAt(i);

                    // there should only be one of a given type, so just return now
                    return;
                }
            }
            throw new Exception("Unable to find a game stat of type '"+type+"' to remove!");
        }
        /// <summary>
        /// This will Suspend all active states, and then Start the state that matches Type T.
        /// This is the recommended way of starting game states,
        /// </summary>
        /// <typeparam name="T">The type of game state to start.</typeparam>
        public void StartState <T>() where T : BaseGameState
        {
            // deactivate any active states
            for (int i = 0; i < mStates.Count; i++)
                if (mStates[i].Active)
                    mStates[i].Suspend();

            // resume the specified state
            for (int i = 0; i < mStates.Count; i++)
                if (mStates[i].GetType() == typeof(T))
                    mStates[i].Start();
        }
        /// <summary>
        /// Returns the instance that matches type T, or null if no such type
        /// is registered.
        /// </summary>
        /// <typeparam name="T">Type of state to get</typeparam>
        /// <returns>The instance of the indicated state, or null if not found</returns>
        public T    GetState   <T>() where T : BaseGameState
        {
            // return the specified state
            for (int i = 0; i < mStates.Count; i++)
                if (mStates[i].GetType() == typeof(T))
                    return (T)mStates[i];

            return null;
        }
        /// <summary>
        /// Calls Update on all active game states.
        /// </summary>
        /// <param name="aTime">The XNA GameTime for the current frame.</param>
        public void Update(GameTime aTime)
        {
            for (int i = 0; i < mStates.Count; i++)
            {
                if (mStates[i].Active)
                    mStates[i].Update(aTime);
            }
        }
        /// <summary>
        /// Calls Draw on all active game states.
        /// </summary>
        /// <param name="aTime">The XNA GameTime for the current frame.</param>
        public void Draw  (GameTime aTime)
        {
            for (int i = 0; i < mStates.Count; i++)
            {
                if (mStates[i].Active)
                    mStates[i].Draw(aTime);
            }
        }
        #endregion
    }
}
